package com.scheduling.schedule.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scheduling.machine.entity.Machine;
import com.scheduling.machine.entity.MachineType;
import com.scheduling.machine.mapper.MachineMapper;
import com.scheduling.machine.mapper.MachineTypeMapper;
import com.scheduling.product.entity.Operation;
import com.scheduling.product.entity.ProcessRoute;
import com.scheduling.product.mapper.OperationMapper;
import com.scheduling.product.mapper.ProcessRouteMapper;
import com.scheduling.schedule.entity.ProductionTask;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 排产数据服务
 * 提供排产算法所需的真实数据库数据
 *
 * @author 开发团队
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScheduleDataService {

    private final MachineMapper machineMapper;
    private final MachineTypeMapper machineTypeMapper;
    private final OperationMapper operationMapper;
    private final ProcessRouteMapper processRouteMapper;

    /**
     * 获取可用机器列表
     *
     * @return 可用机器列表
     */
    public List<Machine> getAvailableMachines() {
        log.debug("查询可用机器列表");

        try {
            // 使用QueryWrapper查询状态为"空闲"的机器
            QueryWrapper<Machine> queryWrapper = new QueryWrapper<>();
            queryWrapper.in("status", "空闲", "维护")
                    .orderBy(true, true, "machine_name");

            List<Machine> machines = machineMapper.selectList(queryWrapper);
            log.debug("查询到 {} 台可用机器", machines.size());
            return machines;

        } catch (Exception e) {
            log.error("查询可用机器失败", e);
            throw new RuntimeException("查询可用机器失败: " + e.getMessage());
        }
    }

    /**
     * 根据机器类型获取机器列表
     *
     * @param machineTypeId 机器类型ID
     * @return 机器列表
     */
    public List<Machine> getMachinesByType(String machineTypeId) {
        log.debug("查询机器类型 {} 的机器列表", machineTypeId);

        try {
            QueryWrapper<Machine> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("machine_type_id", machineTypeId)
                    .orderBy(true, true, "machine_name");

            List<Machine> machines = machineMapper.selectList(queryWrapper);
            log.debug("机器类型 {} 有 {} 台机器", machineTypeId, machines.size());
            return machines;

        } catch (Exception e) {
            log.error("查询机器类型机器失败，类型ID: {}", machineTypeId, e);
            throw new RuntimeException("查询机器类型机器失败: " + e.getMessage());
        }
    }

    /**
     * 获取产品的工艺路线
     *
     * @param productId 产品ID
     * @return 工艺路线
     */
    public ProcessRoute getProcessRoute(String productId) {
        log.debug("查询产品 {} 的工艺路线", productId);

        try {
            QueryWrapper<ProcessRoute> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("product_id", productId);

            ProcessRoute processRoute = processRouteMapper.selectOne(queryWrapper);
            if (processRoute == null) {
                log.warn("产品 {} 没有配置工艺路线", productId);
                throw new RuntimeException("产品 " + productId + " 没有配置工艺路线");
            }

            log.debug("产品 {} 的工艺路线: {}", productId, processRoute.getRouteName());
            return processRoute;

        } catch (Exception e) {
            log.error("查询产品工艺路线失败，产品ID: {}", productId, e);
            throw new RuntimeException("查询产品工艺路线失败: " + e.getMessage());
        }
    }

    /**
     * 获取工艺路线的所有工序
     *
     * @param routeId 工艺路线ID
     * @return 工序列表（按序号排序）
     */
    public List<Operation> getOperationsByRoute(String routeId) {
        log.debug("查询工艺路线 {} 的工序列表", routeId);

        try {
            QueryWrapper<Operation> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("route_id", routeId)
                    .orderBy(true, true, "sequence_no");

            List<Operation> operations = operationMapper.selectList(queryWrapper);
            log.debug("工艺路线 {} 有 {} 个工序", routeId, operations.size());
            return operations;

        } catch (Exception e) {
            log.error("查询工艺路线工序失败，路线ID: {}", routeId, e);
            throw new RuntimeException("查询工艺路线工序失败: " + e.getMessage());
        }
    }

    /**
     * 根据产品ID直接查询所有工序
     *
     * @param productId 产品ID
     * @return 工序列表（按序号排序）
     */
    public List<Operation> getOperationsByProductId(String productId) {
        log.debug("查询产品 {} 的所有工序", productId);

        try {
            List<Operation> operations = operationMapper.selectOperationsByProductId(productId);
            log.debug("产品 {} 有 {} 个工序", productId, operations.size());
            return operations;

        } catch (Exception e) {
            log.error("查询产品工序失败，产品ID: {}", productId, e);
            throw new RuntimeException("查询产品工序失败: " + e.getMessage());
        }
    }

    /**
     * 获取工序的机器类型要求
     *
     * @param operationId 工序ID
     * @return 机器类型ID
     */
    public String getOperationMachineType(String operationId) {
        log.debug("查询工序 {} 的机器类型要求", operationId);

        try {
            Operation operation = operationMapper.selectById(operationId);
            if (operation == null) {
                log.warn("工序 {} 不存在", operationId);
                throw new RuntimeException("工序 " + operationId + " 不存在");
            }

            String machineTypeId = operation.getMachineTypeId();
            log.debug("工序 {} 需要机器类型: {}", operationId, machineTypeId);
            return machineTypeId;

        } catch (Exception e) {
            log.error("查询工序机器类型失败，工序ID: {}", operationId, e);
            throw new RuntimeException("查询工序机器类型失败: " + e.getMessage());
        }
    }

    /**
     * 获取指定机器类型的空闲机器
     *
     * @param machineTypeId 机器类型ID
     * @return 空闲机器ID列表
     */
    public List<String> getIdleMachinesByType(String machineTypeId) {
        log.debug("查询机器类型 {} 的空闲机器", machineTypeId);

        try {
            List<String> idleMachines = operationMapper.selectIdleMachinesByType(machineTypeId);
            log.debug("机器类型 {} 有 {} 台空闲机器", machineTypeId, idleMachines.size());
            return idleMachines;

        } catch (Exception e) {
            log.error("查询空闲机器失败，机器类型ID: {}", machineTypeId, e);
            throw new RuntimeException("查询空闲机器失败: " + e.getMessage());
        }
    }

    /**
     * 获取机器类型信息
     *
     * @param machineTypeId 机器类型ID
     * @return 机器类型
     */
    public MachineType getMachineType(String machineTypeId) {
        log.debug("查询机器类型信息: {}", machineTypeId);

        try {
            MachineType machineType = machineTypeMapper.selectById(machineTypeId);
            if (machineType == null) {
                log.warn("机器类型 {} 不存在", machineTypeId);
                throw new RuntimeException("机器类型 " + machineTypeId + " 不存在");
            }

            return machineType;

        } catch (Exception e) {
            log.error("查询机器类型失败，类型ID: {}", machineTypeId, e);
            throw new RuntimeException("查询机器类型失败: " + e.getMessage());
        }
    }

    /**
     * 获取所有机器类型
     *
     * @return 机器类型列表
     */
    public List<MachineType> getAllMachineTypes() {
        log.debug("查询所有机器类型");

        try {
            List<MachineType> machineTypes = machineTypeMapper.selectList(null);
            log.debug("查询到 {} 种机器类型", machineTypes.size());
            return machineTypes;

        } catch (Exception e) {
            log.error("查询所有机器类型失败", e);
            throw new RuntimeException("查询所有机器类型失败: " + e.getMessage());
        }
    }

    /**
     * 获取机器效率信息
     * 基于机器类型和具体机器的效率配置
     *
     * @param machineId 机器ID
     * @return 效率系数（1.0为标准效率）
     */
    public double getMachineEfficiency(String machineId) {
        log.debug("查询机器 {} 的效率信息", machineId);

        try {
            Machine machine = machineMapper.selectById(machineId);
            if (machine == null) {
                log.warn("机器 {} 不存在，使用默认效率", machineId);
                return 1.0;
            }

            // 考虑机器状态对效率的影响
            double efficiency = 1.0;
            switch (machine.getStatus()) {
                case "空闲":
                    efficiency = 1.0;
                    break;
                case "维护":
                    efficiency = 0.8; // 维护状态效率降低
                    break;
                case "生产中":
                    efficiency = 1.0; // 正在生产，正常效率
                    break;
                case "故障":
                    efficiency = 0.0; // 故障状态无法使用
                    break;
                default:
                    efficiency = 1.0;
            }

            log.debug("机器 {} (状态: {}) 效率系数: {}", machineId, machine.getStatus(), efficiency);
            return efficiency;

        } catch (Exception e) {
            log.error("查询机器效率失败，机器ID: {}", machineId, e);
            return 1.0; // 发生错误时返回默认效率
        }
    }

    /**
     * 获取机器的标准时间配置
     *
     * @param machineId   机器ID
     * @param operationId 工序ID
     * @return 标准时间（分钟）
     */
    public int getStandardTime(String machineId, String operationId) {
        log.debug("查询机器 {} 工序 {} 的标准时间", machineId, operationId);

        try {
            // 首先查询工序的标准时间
            Operation operation = operationMapper.selectById(operationId);
            if (operation != null && operation.getStandardTime() != null) {
                return operation.getStandardTime();
            }

            // 如果工序没有配置标准时间，使用默认值
            log.debug("工序 {} 未配置标准时间，使用默认值60分钟", operationId);
            return 60; // 默认60分钟

        } catch (Exception e) {
            log.error("查询标准时间失败，机器ID: {}, 工序ID: {}", machineId, operationId, e);
            return 60; // 发生错误时返回默认值
        }
    }

    /**
     * 按机器类型分组机器
     *
     * @return 机器类型 -> 机器列表的映射
     */
    public Map<String, List<Machine>> groupMachinesByType() {
        log.debug("按机器类型分组机器");

        List<Machine> allMachines = getAvailableMachines();
        Map<String, List<Machine>> groupedMachines = allMachines.stream()
                .collect(Collectors.groupingBy(Machine::getMachineTypeId));

        log.debug("机器分组完成，共 {} 种类型", groupedMachines.size());
        return groupedMachines;
    }

    /**
     * 验证机器是否可以执行指定工序
     *
     * @param machineId   机器ID
     * @param operationId 工序ID
     * @return 是否可以执行
     */
    public boolean canMachineExecuteOperation(String machineId, String operationId) {
        log.debug("验证机器 {} 是否可以执行工序 {}", machineId, operationId);

        try {
            Machine machine = machineMapper.selectById(machineId);
            if (machine == null || "故障".equals(machine.getStatus())) {
                return false;
            }

            String requiredMachineType = getOperationMachineType(operationId);
            boolean canExecute = machine.getMachineTypeId().equals(requiredMachineType);

            log.debug("机器 {} {}执行工序 {}", machineId, canExecute ? "可以" : "不可以", operationId);
            return canExecute;

        } catch (Exception e) {
            log.error("验证机器工序兼容性失败，机器ID: {}, 工序ID: {}", machineId, operationId, e);
            return false;
        }
    }

    /**
     * 加权平均设备利用率计算
     * 公式：Σ(每台机器工作时间 / 该机器参与时间) / 参与机器数量
     *
     * @param productionTasks 生产任务列表
     * @param startTime       排产开始时间
     * @param endTime         排产结束时间
     * @return 加权平均设备利用率(百分比)
     */
    public BigDecimal calculateTotalUtilization(List<ProductionTask> productionTasks,
                                                LocalDateTime startTime,
                                                LocalDateTime endTime) {
        if (productionTasks == null || productionTasks.isEmpty()) {
            return BigDecimal.ZERO;
        }

        // 按机器分组统计
        Map<String, List<ProductionTask>> tasksByMachine = productionTasks.stream()
                .filter(task -> task.getMachineId() != null)
                .collect(Collectors.groupingBy(ProductionTask::getMachineId));

        if (tasksByMachine.isEmpty()) {
            return BigDecimal.ZERO;
        }

        BigDecimal totalUtilizationSum = BigDecimal.ZERO;
        int participatingMachines = 0;

        // 计算每台机器的利用率
        for (Map.Entry<String, List<ProductionTask>> entry : tasksByMachine.entrySet()) {
            String machineId = entry.getKey();
            List<ProductionTask> machineTasks = entry.getValue();

            // 计算该机器的工作时间
            long machineWorkMinutes = machineTasks.stream()
                    .mapToLong(task -> task.getPlannedTime() != null ? task.getPlannedTime() : 0)
                    .sum();

            // 计算该机器的参与时间(第一个任务开始到最后一个任务结束)
            LocalDateTime machineStartTime = machineTasks.stream()
                    .map(ProductionTask::getPlanStartTime)
                    .filter(Objects::nonNull)
                    .min(LocalDateTime::compareTo)
                    .orElse(startTime);

            LocalDateTime machineEndTime = machineTasks.stream()
                    .map(ProductionTask::getPlanEndTime)
                    .filter(Objects::nonNull)
                    .max(LocalDateTime::compareTo)
                    .orElse(endTime);

            long machineParticipationMinutes = Duration.between(machineStartTime, machineEndTime).toMinutes();

            // 计算该机器的利用率
            if (machineParticipationMinutes > 0) {
                BigDecimal machineUtilization = BigDecimal.valueOf(machineWorkMinutes)
                        .divide(BigDecimal.valueOf(machineParticipationMinutes), 6, RoundingMode.HALF_UP);

                totalUtilizationSum = totalUtilizationSum.add(machineUtilization);
                participatingMachines++;

                // 详细日志记录
                log.debug("机器 {} 利用率计算: 工作时间={}分钟, 参与时间={}分钟, 利用率={:.2f}%",
                        machineId, machineWorkMinutes, machineParticipationMinutes,
                        machineUtilization.multiply(BigDecimal.valueOf(100)));
            }
        }

        // 计算加权平均利用率
        if (participatingMachines == 0) {
            return BigDecimal.ZERO;
        }

        BigDecimal averageUtilization = totalUtilizationSum
                .divide(BigDecimal.valueOf(participatingMachines), 4, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        return averageUtilization;
    }
} 